;;; guile-openai --- An OpenAI API client for Guile
;;; Copyright © 2023 Andrew Whatson <whatson@tailcall.au>
;;;
;;; This file is part of guile-openai.
;;;
;;; guile-openai is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU Affero General Public License as
;;; published by the Free Software Foundation, either version 3 of the
;;; License, or (at your option) any later version.
;;;
;;; guile-openai is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; Affero General Public License for more details.
;;;
;;; You should have received a copy of the GNU Affero General Public
;;; License along with guile-openai.  If not, see
;;; <https://www.gnu.org/licenses/>.

(define-module (openai edit)
  #:use-module (openai api edit)
  #:use-module (openai client)
  #:use-module (ice-9 match)
  #:use-module (srfi srfi-9)
  #:use-module (srfi srfi-9 gnu)
  #:export (openai-default-text-edit-model
            openai-default-text-edit-temperature
            openai-default-text-edit-top-p

            openai-default-code-edit-model
            openai-default-code-edit-temperature
            openai-default-code-edit-top-p

            edit?
            edit-content

            openai-text-edit
            openai-code-edit))

(define-once openai-default-text-edit-model
  (make-parameter 'text-davinci-edit-001))

(define-once openai-default-text-edit-temperature
  (make-parameter *unspecified*))

(define-once openai-default-text-edit-top-p
  (make-parameter *unspecified*))

(define-once openai-default-code-edit-model
  (make-parameter 'code-davinci-edit-001))

(define-once openai-default-code-edit-temperature
  (make-parameter *unspecified*))

(define-once openai-default-code-edit-top-p
  (make-parameter *unspecified*))

(define-record-type <Edit>
  (%make-edit content)
  edit?
  (content edit-content))

(define (print-edit edit port)
  (newline port)
  (display (edit-content edit)))

(set-record-type-printer! <Edit> print-edit)

(define* (openai-text-edit input instruction #:key
                           (model       (openai-default-text-edit-model))
                           (n           *unspecified*)
                           (temperature (openai-default-text-edit-temperature))
                           (top-p       (openai-default-text-edit-top-p)))
  "Send an edit request.  Returns an edit record.

The INPUT must be a string, which is the text to be edited.  The
INSTRUCTION is a string describing the changes to be made.

The keyword arguments correspond to the request parameters described
in the edit request documentation:

#:model - A symbol or string identifying the model to use.  Defaults
to `text-davinci-edit-001.

#:n - The number of responses to generate, returned as multiple
values.

#:temperature - The sampling temperature to use, a number between 0
and 2.

#:top-p - An alternative sampling parameter, a number between 0 and
1."
  (%openai-edit input instruction model n temperature top-p))

(define* (openai-code-edit input instruction #:key
                           (model       (openai-default-code-edit-model))
                           (n           *unspecified*)
                           (temperature (openai-default-code-edit-temperature))
                           (top-p       (openai-default-code-edit-top-p)))
  "Send an edit request.  Returns an edit record.

The INPUT must be a string, which is the code to be edited.  The
INSTRUCTION is a string describing the changes to be made.

The keyword arguments correspond to the request parameters described
in the edit request documentation:

#:model - A symbol or string identifying the model to use.  Defaults
to `code-davinci-edit-001.

#:n - The number of responses to generate, returned as multiple
values.

#:temperature - The sampling temperature to use, a number between 0
and 2.

#:top-p - An alternative sampling parameter, a number between 0 and
1."
  (%openai-edit input instruction model n temperature top-p))

(define (%openai-edit input instruction model n temperature top-p)
  (let* ((model (if (symbol? model) (symbol->string model) model))
         (request (make-edit-request model input instruction
                                     n temperature top-p))
         (response (send-edit-request request)))
    (apply values (map (lambda (choice)
                         (%make-edit (edit-choice-text choice)))
                       (edit-response-choices
                        (json->edit-response response))))))
